﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
using Pixysoft.Framework.Configurations;
using Pixysoft.Framework.Remoting.Controller;
using Pixysoft.Net.Crawler;
using Pixysoft.Framework.Verifications;


namespace Pixysoft.Framework.Remoting.Core
{

    class CacheRemoteChannel<T> : RealProxy, IRemoteChannel<T>
    {
        private static object syncRoot = new Object();

        IRemotingCacheSetting setting = null;

        string token = null;
        string url;
        string username;
        string md5password;


        Type type = null;
        List<HttpPoster> posters = new List<HttpPoster>();

        public CacheRemoteChannel(string url, IRemotingCacheSetting setting)
            : this(url, null, setting)
        {
        }

        public CacheRemoteChannel(string url, string token, IRemotingCacheSetting setting)
            : base(typeof(T))
        {
            this.url = url;
            this.token = token;
            this.setting = setting;
            this.type = typeof(T);
        }


        /// <summary>
        /// 缓存操作不支持直接返回token
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns></returns>
        public string Login(string username, string password)
        {
            if (!Verification.Verifies(username, password).IsNotNullOrEmpty().Result())
                throw Exceptions.VerificationFailedException(username, password);

            this.username = username;

            this.md5password = Pixysoft.Security.MD5.GetMD5(password);

            return token;
        }

        public bool IsLogin()
        {
            return !(string.IsNullOrEmpty(username) && string.IsNullOrEmpty(md5password));
        }

        public void Logout()
        {
            if (string.IsNullOrEmpty(token))
                return;

            string xmlresponse = GetXmlResponse(url, CommandController.Instance.CreateSimpleRequest(token, RemoteCommand.Logout.ToString(), null));
        }

        public void Dispose()
        {
            foreach (HttpPoster poster in posters.ToArray())
            {
                try
                {
                    poster.Close();
                }
                catch
                {
                }
            }

            lock (syncRoot)
            {
                this.posters.Clear();
            }

            this.setting = null;
        }

        public T RemoteProxy
        {
            get { return (T)this.GetTransparentProxy(); }
        }

        public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage methodCall = msg as IMethodCallMessage;

            MethodInfo method = methodCall.MethodBase as MethodInfo;

            if (method == null)
                return new ReturnMessage(Exceptions.UnexpectedResultException("only support method."), methodCall);

            if (method == typeof(object).GetMethod("GetType"))
            {
                return new ReturnMessage(type, null, 0, null, methodCall);
            }
            else if (method == typeof(object).GetMethod("Equals", new Type[] { typeof(object) }))
            {
                return new ReturnMessage(false, null, 0, null, methodCall);
            }
            else if (method == typeof(object).GetMethod("GetHashCode"))
            {
                return new ReturnMessage(this.GetHashCode(), null, 0, null, methodCall);
            }
            else if (method == typeof(object).GetMethod("ToString"))
            {
                return new ReturnMessage(this.ToString(), null, 0, null, methodCall);
            }


            //调用序列化

            IXmlNode node = null;

            try
            {
                node = ReflectionController.Instance.GetMethodDeclaration(method, methodCall.Args);
            }
            catch (Exception ex)
            {
                return new ReturnMessage(ex, methodCall);
            }

            string rawrequest = node.Serialize();

            string rawresponse = null;

            if (CacheController.Instance.Contains(rawrequest) && !CacheController.Instance.IsOverdued(rawrequest))
            {
                LoggerHelper.Info("begin load buffer value from cache, rawrequest:{0}", rawrequest);

                rawresponse = CacheController.Instance.GetMethodResponse(rawrequest);
            }
            else
            {
                try
                {
                    rawresponse = RemotingInvoke(method, rawrequest);
                }
                catch (Exception ex)
                {
                    return new ReturnMessage(ex, methodCall);
                }
            }

            //返回远程结果

            if (method.ReturnType.Equals(typeof(void)))
                return new ReturnMessage(null, null, 0, null, methodCall);

            object returnvalue = null;

            try
            {
                returnvalue = ReflectionController.Instance.GetDeserializeRemoteCallResult(method.ReturnType, rawresponse);
            }
            catch (Exception ex)
            {
                return new ReturnMessage(ex, methodCall);
            }

            return new ReturnMessage(returnvalue, null, 0, null, methodCall);
        }

        private string RemotingInvoke(MethodInfo method, string rawrequest)
        {
            if (string.IsNullOrEmpty(token))
            {
                if (!Verification.Verifies(username, md5password).IsNotNullOrEmpty().Result())
                {
                    throw Exceptions.UnexpectedResultException("missing username/md5password");
                }

                string xmlLoginRequest = CommandController.Instance.CreateLoginRequest(username, md5password);

                string xmlLoginResponse = GetXmlResponse(url, xmlLoginRequest);

                IXmlNode loginResponse = CommandController.Instance.Deserialize(xmlLoginResponse);

                if (loginResponse == null)
                    throw Exceptions.UnexpectedResultException(xmlLoginResponse);

                this.token = loginResponse.Text;
            }

            string compresscontent = Pixysoft.IO.Compressor.Instance.Compress(rawrequest);
            string encryptcontent = Pixysoft.Security.DES.Encrypt(compresscontent, StringHelper.global_framework_id);
            string base64content = Pixysoft.Security.Base64.Encript(encryptcontent);
            IXmlNode request = CommandController.Instance.CreateRequest(token, RemoteCommand.RemoteCall.ToString(), base64content);

            //远程访问

            string rawresponse = null;

            string xmlInvokeResponse = GetXmlResponse(url, request.Serialize());

            base64content = CommandController.Instance.Deserialize(xmlInvokeResponse).Text;

            if (string.IsNullOrEmpty(base64content))
            {
                return null;
            }
            else
            {
                encryptcontent = Pixysoft.Security.Base64.Decript(base64content);
                compresscontent = Pixysoft.Security.DES.Decrypt(encryptcontent, StringHelper.global_framework_id);
                rawresponse = Pixysoft.IO.Compressor.Instance.DeCompress(compresscontent);
            }


            if (string.IsNullOrEmpty(rawresponse))
            {
                CacheController.Instance.AddNullCache(method.Name, rawrequest);
            }
            else
            {
                switch (setting.CacheMode)
                {
                    case RemotingCacheMode.DeepCache:
                        {
                            CacheController.Instance.AddDeepCache(setting.TimeSpan, method.Name, rawrequest, rawresponse);
                            break;
                        }
                    case RemotingCacheMode.ShallowCache:
                    default:
                        {
                            CacheController.Instance.AddShallowCache(method.Name, rawrequest, rawresponse);

                            break;
                        }
                }
            }

            LoggerHelper.Info("begin cache buffer value to cache, rawrequest:{0},rawresponse:{1}", rawrequest, rawresponse);

            return rawresponse;
        }

        private string GetXmlResponse(string url, string message)
        {
            LoggerHelper.Debug("CP.REQUEST:" + message);

            HttpResult result = null;

            HttpPoster poster = new HttpPoster();

            posters.Add(poster);

            try
            {
                result = poster.Post(url, message);
            }
            catch (System.Net.WebException ex)
            {
                LoggerHelper.Info(ex.Message);

                throw Exceptions.ExternalException(ex).Handle();
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                LoggerHelper.Info(ex.Message);

                throw Exceptions.ExternalException(ex).Handle();
            }
            catch (System.InvalidOperationException ex)
            {
                LoggerHelper.Info(ex.Message);

                throw Exceptions.ExternalException(ex).Handle();
            }
            catch (System.Threading.ThreadAbortException ex)
            {
                LoggerHelper.Info(ex.Message);

                throw Exceptions.ExternalException(ex).Handle();
            }
            catch (Exception ex)
            {
                LoggerHelper.Info(ex.Message);

                if (Exceptions.IsHandled(ex))
                    throw ex;

                throw Exceptions.Parse(ex);
            }
            finally
            {
                posters.Remove(poster);
            }

            if (!CommandController.Instance.IsSucceed(result.Response))
                throw Exceptions.ExternalException(result.Response);

            LoggerHelper.Debug("CP.RESPONSE:" + result.Response);

            return result.Response;
        }

    }
}
